package com.wuyan.web.form.service;

import com.wuyan.helper.kit.StringHelper;
import com.wuyan.helper.kit.endecryption.EncryptionHelper;
import com.wuyan.web.base.helper.auth.LoginInfo;
import com.wuyan.web.base.helper.rep.RepPageData;
import com.wuyan.web.base.helper.req.CustomQueryOrderParams;
import com.wuyan.web.base.helper.req.CustomQueryParams;
import com.wuyan.web.form.api.FormApi;
import com.wuyan.web.form.entity.PubForm;
import com.wuyan.web.form.helper.FormHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.wuyan.web.form.service.FormService.*;

@Service
@Slf4j
public class FlowService {

    @Autowired
    private FormApi formApi;

    @Autowired
    private FormHelper formHelper;

    @Autowired
    private FormService formService;

    /**
     * 开始一个新的处理流程
     * 需要注意的是，新的流程应该保存两条过程信息，一是开始节点信息，二是下一个节点信息
     *
     * @param data 数据信息
     * @return Map<?, ?>
     */
    public Map<String, Object> create(Map<String, Object> data, LoginInfo loginInfo) throws Exception {
        // ID不存在则生成
        String dataId;
        if (!data.containsKey(DEF_ID_NAME)) {
            dataId = EncryptionHelper.getInstance().md5(UUID.randomUUID().toString());
            data.put(DEF_ID_NAME, dataId);
        } else {
            dataId = data.get(DEF_ID_NAME).toString();
        }

        String flowId = String.valueOf(data.get(DEF_CFG_FLOW_ID_NAME));
        String flowTableName = formHelper.getFlowTableName();
        String flowNodeTableName = formHelper.getFlowNodeTableName();

        // 流程配置信息
        Map flow = formService.get(flowId, flowTableName);
        ArrayList<Map> nodeList = null, lineList = null;
        if (flow.containsKey("node_list") && flow.get("node_list") instanceof ArrayList) {
            nodeList = (ArrayList<Map>) flow.get("node_list");
        }
        if (flow.containsKey("line_list") && flow.get("line_list") instanceof ArrayList) {
            lineList = (ArrayList<Map>) flow.get("line_list");
        }

        assert nodeList != null : "未配置节点";
        assert lineList != null : "未配置节点连接";

        /*
            分别取两端节点信息以及连接线信息
         */
        Map startNode = startNode(nodeList);
        Map startLine = startLine(lineList, startNode);
        Map nextNode = nextNode(nodeList, startLine);
        assert nextNode != null : "未配置后续流程，无法开始";

        /*
            构建当前节点以及被激活节点信息，以便存储
         */
        Map<String, Object> currentNode = currentNode(dataId, flow, startNode, null, loginInfo, null);
        Map<String, Object> activatedNode = currentNode(dataId, flow, nextNode, startLine, loginInfo, currentNode);

        // 取表单配置信息
        PubForm pubForm = formApi.tableExists(flowNodeTableName);

        // 同步原始数据信息
        data.put(DEF_CFG_STATUS_NAME, activatedNode.get("status_value"));
        data.put(DEF_CFG_STATUS_NAME_NAME, activatedNode.get("status_name"));
        // 如果没有后续处理了，则标记结束
        if (getNextNodeListByCurrentId(nodeList, lineList, activatedNode.get("node_id").toString()).size() < 1) {
            data.put(DEF_CFG_FINISH_NAME, true);
            activatedNode.put(DEF_CFG_ACTIVE_NAME, false);
        }

        // 入库
        formService.create(flowNodeTableName, currentNode, loginInfo, pubForm);
        formService.create(flowNodeTableName, activatedNode, loginInfo, pubForm);

        return data;
    }

    /**
     * 流程处理
     *
     * @param dataId    数据ID
     * @param lineId    节点前的连线ID
     * @param nodeId    节点ID
     * @param loginInfo 登录信息
     * @param reqData   附加信息
     * @return Boolean
     */
    public Map<String, Object> handle(String table, String dataId,
                                      String lineId, String nodeId,
                                      LoginInfo loginInfo,
                                      Map<String, Object> reqData) throws Exception {
        Map data = formService.get(dataId, table);
        String flowId = String.valueOf(data.get(DEF_CFG_FLOW_ID_NAME));
        return handleByFlowId(flowId, dataId, lineId, nodeId, loginInfo, table, data, reqData);
    }

    /**
     * 流程处理
     *
     * @param flowId    流程ID
     * @param dataId    数据ID
     * @param lineId    节点前的连线ID
     * @param nodeId    节点ID
     * @param loginInfo 登录信息
     * @param reqData   附加信息
     * @return Boolean
     */
    public Map<String, Object> handleByFlowId(String flowId, String dataId, String lineId,
                                              String nodeId, LoginInfo loginInfo,
                                              String table, Map data, Map<String, Object> reqData) throws Exception {
        String flowTableName = formHelper.getFlowTableName();
        String flowNodeTableName = formHelper.getFlowNodeTableName();

        // 流程配置信息
        Map flow = formService.get(flowId, flowTableName);
        ArrayList<Map> nodeList = null, lineList = null;
        if (flow.containsKey("node_list") && flow.get("node_list") instanceof ArrayList) {
            nodeList = (ArrayList<Map>) flow.get("node_list");
        }
        if (flow.containsKey("line_list") && flow.get("line_list") instanceof ArrayList) {
            lineList = (ArrayList<Map>) flow.get("line_list");
        }
        assert nodeList != null : "未配置节点";
        assert lineList != null : "未配置节点连接";

        // 获取节点和连线的配置
        Map node = getListById(nodeList, nodeId);
        Map line = getListById(lineList, lineId);
        assert node != null : "未配置后续流程，无法继续";
        assert line != null : "未正常连接流程，无法继续";

        // 当前进行中的节点
        Map<String, Object> currentNode = getCurrentNode(dataId);
        assert currentNode != null : "当前无法找到上一个节点，无法继续";
        Map<String, Object> activatedNode = currentNode
                (dataId, flow, node, line, loginInfo, currentNode);
        Map<String, Object> preprocessNode = null;

        // 取表单配置信息
        PubForm pubForm = formApi.tableExists(flowNodeTableName);

        if (null != data) {
            // 更新状态
            data.put(DEF_CFG_STATUS_NAME, activatedNode.get("status_value"));
        }

        // 备注
        currentNode.put("remark", reqData.containsKey("remark") ? reqData.get("remark").toString() : "");
        currentNode.put("account", loginInfo.getAccount().getId());
        currentNode.put("account_name", StringHelper.getNotEmpty(loginInfo.getUser().getNickname(), loginInfo.getAccount().getUsername()));

        /*
            下一组节点预处理
         */
        {
            String activatedNodeId = activatedNode.get("node_id").toString();
            // 下一组节点
            List<Map> nextNodeList = getNextNodeListByCurrentId
                    (nodeList, lineList, activatedNodeId);
            // 如果没有后续处理了，则标记结束
            if (nextNodeList.size() < 1) {
                if (null != data) {
                    data.put(DEF_CFG_FINISH_NAME, true);
                }
                activatedNode.put(DEF_CFG_ACTIVE_NAME, false);
                activatedNode.put(DEF_CFG_FINISH_NAME, true);
            } else {
                Map nextNode = nextNodeList.get(0);
                Map nextLine = getLineBySourceAndTarget
                        (lineList, activatedNodeId, nextNode.get("id").toString());

                // 驳回操作，并且有回归到开始位置
                if (hasReject(node) && hasStart(nextNode)) {
                    // 构建预处理节点
                    preprocessNode = currentNode
                            (dataId, flow, nextNode, nextLine, loginInfo, activatedNode);

                    if (null != data) {
                        data.put(DEF_CFG_STATUS_NAME, preprocessNode.get("status_value"));
                    }
                }
            }
        }

        /*
            入库
         */
        {
            formService.update(currentNode.get("id").toString(), flowNodeTableName, currentNode);
            activatedNode = formService.create(flowNodeTableName, activatedNode, loginInfo, pubForm);
            if (null != preprocessNode) {
                preprocessNode = formService.create(flowNodeTableName, preprocessNode, loginInfo, pubForm);
            }

            if (null != table && null != data) {
                formService.update(dataId, table, data);
            }
        }

        return null == preprocessNode ? activatedNode : preprocessNode;
    }

    /**
     * 是否为驳回节点
     *
     * @param node 节点信息
     * @return boolean
     */
    private boolean hasReject(Map node) {
        return "start".equals(node.get("type")) && "end".equals(node.get("status"));
    }

    /**
     * 是否为开始节点
     *
     * @param node 节点
     * @return boolean
     */
    private boolean hasStart(Map node) {
        return "start".equals(node.get("type")) && "start".equals(node.get("status"));
    }

    /**
     * 创建节点信息
     *
     * @param dataId    shujuID
     * @param flow      流程信息
     * @param node      激活节点
     * @param line      连线信息
     * @param loginInfo 用户
     * @param lastNode  上一个节点
     * @return Map<String, Object>
     */
    private Map<String, Object> currentNode(String dataId, Map flow,
                                            Map node, Map line,
                                            LoginInfo loginInfo,
                                            Map<String, Object> lastNode) {
        Map<String, Object> currentNode = new HashMap<>();

        // 数据
        currentNode.put("data_id", dataId);

        // 流程信息
        currentNode.put("flow_id", flow.get("id"));
        currentNode.put("flow_name", flow.get("name"));

        // 节点
        currentNode.put("node_id", node.get("id"));
        currentNode.put("node_name", node.get("name"));
        currentNode.put("node_type", node.get("type"));
        currentNode.put("node_status", node.get("status"));
        currentNode.put("node_icon", node.get("icon"));

        // 处理信息
        currentNode.put("dept", node.get("dept"));
        currentNode.put("post", node.get("post"));
        currentNode.put("account", node.containsKey("account") && StringUtils.isNotBlank(node.get("account").toString())
                ? node.get("account")
                : "");

        // 连接线, 兼容新的流程处理
        if (null == line) {
            currentNode.put("status_name", node.get("name"));
            currentNode.put("status_value", "0");
        } else {
            currentNode.put("line_id", line.get("id"));
            currentNode.put("node_source", line.get("source"));
            currentNode.put("status_name", line.get("label"));
            currentNode.put("status_value", line.get("value"));
        }

        // 其它
        int sort = 1;
        if (null != lastNode) {
            sort = (int) lastNode.get("cfg_sort");
            sort++;
            lastNode.put(DEF_CFG_ACTIVE_NAME, false);
        }

        currentNode.put("cfg_sort", sort);
        currentNode.put(DEF_CFG_ACTIVE_NAME, true);
        return currentNode;
    }


    /**
     * 根据ID取元素
     *
     * @param list 数组
     * @param id   ID
     * @return Map<?, ?>
     */
    private Map getListById(ArrayList<Map> list, String id) {
        for (Map map : list) {
            if (id.equals(map.get("id"))) {
                return map;
            }
        }

        return null;
    }

    /**
     * 根据source和target取连线信息
     *
     * @param lineList 连线组
     * @param source   源
     * @param target   目标
     * @return Map
     */
    private Map getLineBySourceAndTarget(ArrayList<Map> lineList, String source, String target) {
        for (Map map : lineList) {
            if (source.equals(map.get("source")) && target.equals(map.get("target"))) {
                return map;
            }
        }

        return null;
    }

    /**
     * 查询开始节点
     *
     * @param nodeList 节点组
     * @return Map<?, ?>
     */
    private Map startNode(ArrayList<Map> nodeList) {
        for (Map map : nodeList) {
            if ("start".equals(map.get("type")) && "start".equals(map.get("status"))) {
                return map;
            }
        }
        return null;
    }

    /**
     * 找到开始节点的连接线，如果存在多条，此处只返回单条
     *
     * @param lineList  线组
     * @param startNode 开始节点
     * @return Map<?, ?>
     */
    private Map startLine(ArrayList<Map> lineList, Map startNode) {
        assert startNode != null : "未配置开始节点";

        for (Map map : lineList) {
            if (startNode.get("id").equals(map.get("source"))) {
                return map;
            }
        }

        return null;
    }

    /**
     * 根据连接线找到下一个节点
     *
     * @param nodeList 节点组
     * @param line     连接线
     * @return Map<?, ?>
     */
    private Map nextNode(ArrayList<Map> nodeList, Map line) {
        assert line != null : "未配置连接线";

        for (Map map : nodeList) {
            if (line.get("target").equals(map.get("id"))) {
                return map;
            }
        }

        return null;
    }

    /**
     * 获取当前进行中的节点信息
     *
     * @param dataId 数据ID
     * @return Map
     */
    private Map<String, Object> getCurrentNode(String dataId) {
        String flowNodeTableName = formHelper.getFlowNodeTableName();

        List<CustomQueryParams> paramsList = new ArrayList<>();
        List<CustomQueryOrderParams> orderList = new ArrayList<>();

        paramsList.add(CustomQueryParams.builder()
                .left("data_id")
                .op("eq")
                .right(new String[]{dataId})
                .build());
        paramsList.add(CustomQueryParams.builder()
                .left("cfg_active")
                .op("eq")
                .right(new Object[]{true})
                .build());

        RepPageData<Map> data = formService.page(flowNodeTableName, true, 1, 1,
                paramsList, orderList);

        List<Map> list = data.getList();
        return list.size() > 0 ? list.get(0) : null;
    }

    /**
     * 获取下一组处理节点
     *
     * @param nodeList  节点组
     * @param lineList  线组
     * @param currentId 当前节点ID
     * @return List<Map>
     */
    private List<Map> getNextNodeListByCurrentId(ArrayList<Map> nodeList,
                                                 ArrayList<Map> lineList,
                                                 String currentId) {
        Map<String, Map> mapNodeList = new HashMap<>();
        nodeList.forEach(t -> mapNodeList.put(t.get("id").toString(), t));

        List<String> nids = lineList.stream().filter(t -> currentId.equals(t.get("source")))
                .map(t -> t.get("target").toString())
                .collect(Collectors.toList());

        return nids.stream().filter(mapNodeList::containsKey)
                .map(mapNodeList::get)
                .collect(Collectors.toList());
    }

    /**
     * 获取下一组处理节点连线信息
     *
     * @param lineList  线组
     * @param currentId 当前节点ID
     * @return List<Map>
     */
    private List<Map> getNextLineListByCurrentId(ArrayList<Map> lineList,
                                                 String currentId) {
        return lineList.stream()
                .filter(t -> currentId.equals(t.get("source")))
                .collect(Collectors.toList());
    }

}
